SPB Git forge

spb/toveli

Public
14commits 1branches 0releases
1.1 MBsize
maindefault branch
5 h agolast push
TypeScript 50.6% JavaScript 46.1% SQL 2.1% Shell 0.6% CSS 0.5%
1.2 KB · 15 lines typescript
Raw Blame History
1import { NextRequest, NextResponse } from "next/server";2import { z } from "zod";3import { assertSameOrigin, requireUser, clientIp } from "@/lib/auth/session";4import { openInquiry } from "@/lib/inquiries/service";5import { hit } from "@/lib/auth/ratelimit";6import { ipHash } from "@/lib/auth/crypto";7import { apiError, handle, parseBody } from "@/lib/api";8export const dynamic = "force-dynamic";9const Body = z.object({ body: z.string().trim().min(10).max(3000), category: z.enum(["INFO", "VISIT", "OFFER"]).default("INFO"), idempotencyKey: z.string().max(80).optional() });10export async function POST(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {11  try { await assertSameOrigin(); const u = await requireUser(); const { id } = await params; const b = await parseBody(req, Body);12    const rl = await hit(`inq:${u.id}`, 20, 3600); const rl2 = await hit(`inq:ip:${ipHash(await clientIp())}`, 40, 3600); if (!rl.ok || !rl2.ok) return apiError(429, "rate_limited", "Trop de demandes envoyées. Réessayez plus tard.");13    const inq = await openInquiry(id, u.id, b.category, b.body, b.idempotencyKey); return NextResponse.json({ ok: true, inquiryId: inq.id }, { status: 201 }); } catch (e) { return handle(e); }14}15